{% extends "../base.html" %}
{% load admin_utils %}

{% block main_content %}
<div id="application-egress">
    <bk-alert type="warning" title="启用 Egress 前需确认：1. 该环境部署的集群支持 BCS Egress，2. 应用在该环境已经成功部署，3. 用户提供的目标服务 Host/Port 是集群内可达的" class="mb15"></bk-alert>
    <bk-alert type="warning" title="注意：任何页面上的副本数量，资源限制，Egress 规则的调整，都需要点击 `保存配置` 按钮才会保存并下发到集群中！"  class="mb15"></bk-alert>
    <bk-collapse accordion @item-click="fetchModuleEgressSpec" v-model="activeModuleName">
        <bk-collapse-item v-for="(module, mIdx) in application.modules" :name="module.name">
            <h4>$[ module.name ] 模块</h4>
            <div slot="content" class="env-egress-spec">
                <h5>预发布环境（Stag）</h5>
                <div v-if="!stagFormData.enabled">
                    <bk-button theme="primary" @click="clickNewSpecButton('stag')">新建配置</bk-button>
                </div>
                <div v-else>
                    <bk-form form-type="inline">
                        <bk-form-item label="副本数量" class="spec-form-item">
                            <bk-input v-model="stagFormData.replicas" type="number" min="1" max="2"></bk-input>
                        </bk-form-item>
                        <bk-form-item label="CPU 限制" class="spec-form-item">
                            <bk-select v-model="stagFormData.cpu_limit" style="width: 150px;">
                                <bk-option
                                    v-for="option in cpuLimitChoices"
                                    :key="option.value"
                                    :id="option.value"
                                    :name="option.name">
                                </bk-option>
                            </bk-select>
                        </bk-form-item>
                        <bk-form-item label="内存限制" class="spec-form-item">
                            <bk-select v-model="stagFormData.memory_limit" style="width: 150px;">
                                <bk-option
                                    v-for="option in memoryLimitChoices"
                                    :key="option.value"
                                    :id="option.value"
                                    :name="option.name">
                                </bk-option>
                            </bk-select>
                        </bk-form-item>
                    </bk-form>
                    <h6>Egress 规则</h6>
                    <bk-table :data="stagFormData.rules" class="mt10">
                        <bk-table-column label="Host" prop="host"></bk-table-column>
                        <bk-table-column label="Port" prop="port"></bk-table-column>
                        <bk-table-column label="Protocol" prop="protocol"></bk-table-column>
                        <bk-table-column label="操作" width="150">
                            <template slot-scope="props">
                                <bk-button theme="primary" text @click="deleteRule('stag', props.row)">删除</bk-button>
                            </template>
                        </bk-table-column>
                    </bk-table>
                    <div class="mt10">
                        <bk-button theme="primary" outline="true" class="mr10" @click="addRule('stag')">添加规则</bk-button>
                        <bk-button theme="primary" outline="true" class="mr10" @click="fetchEgressIps('stag')">获取出口 IP</bk-button>
                        <bk-button theme="primary" outline="true" class="mr10" @click="saveEgress('stag')">保存配置</bk-button>
                        <bk-button theme="danger" outline="true" class="mr10" @click="deleteEgress('stag')">删除配置</bk-button>
                    </div>
                </div>
            </div>
            <div slot="content" class="env-egress-spec">
                <h5>生产环境（Prod）</h5>
                <div v-if="!prodFormData.enabled">
                    <bk-button theme="primary" @click="clickNewSpecButton('prod')">新建配置</bk-button>
                </div>
                <div v-else>
                    <bk-form form-type="inline">
                        <bk-form-item label="副本数量" class="spec-form-item">
                            <bk-input v-model="prodFormData.replicas" type="number" min="1" max="2"></bk-input>
                        </bk-form-item>
                        <bk-form-item label="CPU 限制" class="spec-form-item">
                            <bk-select v-model="prodFormData.cpu_limit" style="width: 150px;">
                                <bk-option
                                    v-for="option in cpuLimitChoices"
                                    :key="option.value"
                                    :id="option.value"
                                    :name="option.name">
                                </bk-option>
                            </bk-select>
                        </bk-form-item>
                        <bk-form-item label="内存限制" class="spec-form-item">
                            <bk-select v-model="prodFormData.memory_limit" style="width: 150px;">
                                <bk-option
                                    v-for="option in memoryLimitChoices"
                                    :key="option.value"
                                    :id="option.value"
                                    :name="option.name">
                                </bk-option>
                            </bk-select>
                        </bk-form-item>
                    </bk-form>
                    <h6>Egress 规则</h6>
                    <bk-table :data="prodFormData.rules" class="mt10">
                        <bk-table-column label="Host" prop="host"></bk-table-column>
                        <bk-table-column label="Port" prop="port"></bk-table-column>
                        <bk-table-column label="Protocol" prop="protocol"></bk-table-column>
                        <bk-table-column label="操作" width="150">
                            <template slot-scope="props">
                                <bk-button theme="primary" text @click="deleteRule('prod', props.row)">删除</bk-button>
                            </template>
                        </bk-table-column>
                    </bk-table>
                    <div class="mt10">
                        <bk-button theme="primary" outline="true" class="mr10" @click="addRule('prod')">添加规则</bk-button>
                        <bk-button theme="primary" outline="true" class="mr10" @click="fetchEgressIps('prod')">获取出口 IP</bk-button>
                        <bk-button theme="primary" outline="true" class="mr10" @click="saveEgress('prod')">保存配置</bk-button>
                        <bk-button theme="danger" outline="true" class="mr10" @click="deleteEgress('prod')">删除配置</bk-button>
                    </div>
                </div>
            </div>
        </bk-collapse-item>
    </bk-collapse>

    <bk-dialog v-model="ruleDialog.visible" header-position="center" width="500" :confirm-fn="submitDialog" ok-text="添加">
        <div slot="header"><h4>添加 Egress 规则</h4></div>
        <bk-form :label-width="120" :model="ruleDialog.form">
            <bk-form-item label="Host" :required="true" desc="目标服务的域名 / IP">
                <bk-input v-model="ruleDialog.form.host"></bk-input>
            </bk-form-item>
            <bk-form-item label="Port" :required="true" desc="目标服务的端口">
                <bk-input v-model="ruleDialog.form.port" type="number" min="1" max="65535"></bk-input>
            </bk-form-item>
            <bk-form-item label="Protocol" :required="true">
                <bk-select v-model="ruleDialog.form.protocol" style="width: 150px;">
                    <bk-option
                        v-for="option in networkProtocolChoices"
                        :key="option.value"
                        :id="option.value"
                        :name="option.name">
                    </bk-option>
                </bk-select>
            </bk-form-item>
        </bk-form>
    </bk-dialog>

</div>
{% endblock %}

{% block main_script %}
<script>
    const application = {{ application | to_json }}

    const URLRouter = {
        detail: decodeURI("{% url 'admin.applications.engine.egress.detail' application.code '${module}' '${env}' %}"),
        fetchIPs: decodeURI("{% url 'admin.applications.engine.egress.ips' application.code '${module}' '${env}' %}"),
    }

    document.addEventListener('DOMContentLoaded', () => {
        new Vue({
            el: "#application-egress",
            delimiters: ['$[', ']'],
            data: function () {
                return {
                    // 组件控制
                    activeModuleName: "default",
                    cpuLimitChoices: [
                        {value: '500m', name: '500m'},
                        {value: '1000m', name: '1000m'},
                        {value: '2000m', name: '2000m'},
                    ],
                    memoryLimitChoices: [
                        {value: '256Mi', name: '256Mi'},
                        {value: '512Mi', name: '512Mi'},
                        {value: '1Gi', name: '1Gi'},
                        {value: '2Gi', name: '2Gi'},
                    ],
                    networkProtocolChoices: [
                        {value: "TCP", name: "TCP"},
                        {value: "UDP", name: "UDP"},
                    ],
                    // 数据部分
                    application,
                    // 表单部分
                    stagFormData: {
                        enabled: false,
                        replicas: 1,
                        cpu_limit: '500m',
                        memory_limit: '256Mi',
                        rules: []
                    },
                    prodFormData: {
                        enabled: false,
                        replicas: 1,
                        cpu_limit: '500m',
                        memory_limit: '256Mi',
                        rules: []
                    },
                    ruleDialog: {
                        visible: false,
                        env: '',
                        form: {}
                    }
                }
            },
            mounted: function () {
                this.fetchModuleEgressSpec(this.activeModuleName)
            },
            methods: {
                // 获取指定环境的表单数据
                getFormDataWithEnv(env) {
                    return (env === 'prod') ? this.prodFormData : this.stagFormData
                },
                // 获取指定的某个模块的 Egress 配置
                fetchModuleEgressSpec(module) {
                    if (module.length === 0) {
                        return
                    }
                    this.stagFormData.enabled = false
                    this.prodFormData.enabled = false
                    this.fetchEnvEgressSpec(module, 'stag')
                    this.fetchEnvEgressSpec(module, 'prod')
                },
                // 获取指定的某个部署环境的 Egress 配置
                fetchEnvEgressSpec(module, env) {
                    let url = URLRouter.detail.replace("${module}", module).replace("${env}", env)
                    let formData = this.getFormDataWithEnv(env)

                    try {
                        this.$http.get(url).then(resp => {
                            formData.enabled = resp.enabled
                            if (!resp.enabled) {
                                return
                            }
                            formData.replicas = resp.replicas
                            formData.cpu_limit = resp.cpu_limit
                            formData.memory_limit = resp.memory_limit
                            formData.rules = resp.rules
                        })
                    } catch (e) {
                        formData.enabled = false
                    }
                },
                // 点击新建按钮触发
                clickNewSpecButton(env) {
                    let formData = this.getFormDataWithEnv(env)
                    formData.replicas = 1
                    formData.cpu_limit = '500m'
                    formData.memory_limit = '256Mi'
                    formData.rules = []
                    formData.enabled = true
                },
                // 点击删除规则触发
                deleteRule(env, row) {
                    let formData = this.getFormDataWithEnv(env)
                    for (let i = 0; i < formData.rules.length; i++) {
                        if (formData.rules[i] === row) {
                            formData.rules.splice(i, 1)
                            break
                        }
                    }
                    this.$bkMessage({theme: 'success', message: `删除规则成功，请点击 "保存配置按钮" 以下发`})
                },
                // 点击添加规则触发
                addRule(env) {
                    this.ruleDialog.visible = true
                    this.ruleDialog.env = env
                    this.resetRuleDialogForm()
                },
                // 重置添加规则的表单
                resetRuleDialogForm() {
                    this.ruleDialog.form = {
                        host: '',
                        port: '',
                        protocol: 'TCP',
                    }
                },
                // 填写完规则保存触发
                submitDialog() {
                    if (!this.ruleDialog.form.host || !this.ruleDialog.form.port) {
                        this.$bkMessage({theme: 'error', message: `Host / Port 为必填项`})
                        return
                    }
                    let formData = this.getFormDataWithEnv(this.ruleDialog.env)
                    formData.rules.push(this.ruleDialog.form)

                    this.ruleDialog.visible = false
                    this.$bkMessage({theme: 'success', message: `添加规则成功，请点击 "保存配置按钮" 以下发`})
                },
                // 点击获取出口 IP 触发
                fetchEgressIps(env) {
                    let url = URLRouter.fetchIPs.replace("${module}", this.activeModuleName).replace("${env}", env)
                    const h = this.$createElement

                    try {
                        this.$http.get(url).then(resp => {
                            this.$bkInfo({
                                width: 400,
                                title: `模块 ${this.activeModuleName} 环境 ${env} 出口 IP 列表`,
                                subHeader: h('div', {
                                    style: {
                                        'color': 'green',
                                        'text-align': 'center'
                                    }
                                }, `${resp.ips}`),
                                showFooter: false,
                            })
                        })
                    } catch (e) {
                        this.$bkMessage({theme: 'error', message: `获取出口 IP 失败`})
                    }
                },
                // 点击保存配置触发
                saveEgress(env) {
                    let url = URLRouter.detail.replace("${module}", this.activeModuleName).replace("${env}", env)
                    let formData = this.getFormDataWithEnv(env)
                    if (formData.rules.length === 0) {
                        this.$bkMessage({theme: 'error', message: `至少需要包含一条 Egress 规则`})
                        return
                    }

                    // 保存需要进行二次确认
                    this.$bkInfo({
                        width: 400,
                        title: `确认保存 Egress 配置并下发到集群？`,
                        confirmLoading: true,
                        confirmFn: async () => {
                            try {
                                this.$http.post(url, formData).then(resp => {
                                    this.$bkMessage({theme: 'success', message: `保存 Egress 配置成功`})
                                }, err => {
                                    errMsg = `请确认应用在该环境已成功部署！`
                                    if (err.detail) {
                                        errMsg = `错误信息：${err.detail}`
                                    }
                                    this.$bkMessage({theme: 'error', message: `保存 Egress 配置失败，${errMsg}`})
                                })
                            } catch (e) {
                                this.$bkMessage({theme: 'error', message: `保存 Egress 配置失败：${e}`})
                            }
                        }
                    })
                },
                // 点击删除配置触发
                deleteEgress(env) {
                    let url = URLRouter.detail.replace("${module}", this.activeModuleName).replace("${env}", env)
                    const h = this.$createElement

                    // 删除需要进行二次确认
                    this.$bkInfo({
                        width: 400,
                        title: `确认删除 Egress 配置？`,
                        subHeader: h('div', {
                            style: {
                                'color': 'red',
                                'text-align': 'center'
                            }
                        }, `即将删除 ${this.activeModuleName} 模块 ${env} 环境 Egress 配置，该操作将导致固定出口 IP 功能失效！`),
                        confirmLoading: true,
                        confirmFn: async () => {
                            try {
                                await this.$http.delete(url).then(resp => {
                                    this.$bkMessage({theme: 'success', message: `删除 Egress 配置成功`})
                                }, err => {
                                    this.$bkMessage({theme: 'error', message: `删除 Egress 配置失败，错误信息：${err.detail}`})
                                })
                            } catch (e) {
                                this.$bkMessage({theme: 'error', message: `删除 Egress 配置失败：${e}`})
                            } finally {
                                // 重新拉取数据
                                this.fetchEnvEgressSpec(this.activeModuleName, env)
                            }
                        }
                    })
                }
            }
        })
    })
</script>
<style>
    .env-egress-spec {
        width: 70%;
        padding: 10px;
    }

    .spec-form-item {
        width: 30%;
        padding: 5px;
    }
</style>
{% endblock %}
